﻿using System;
using Blazor.Diagrams.Components;
using Blazor.Diagrams.Core;
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Models;
using Blazor.Diagrams.Core.Models.Base;
using ERP.Workflow.Designer.Application.Contracts;
using ERP.Workflow.Designer.BlazorUI.Components;
using ERP.Workflow.Designer.BlazorUI.Core;
using ERP.Workflow.Designer.BlazorUI.Domain;
using ERP.Workflow.Designer.BlazorUI.Models;
using ERP.Workflow.Designer.BlazorUI.Provider;
using ERP.Workflow.Designer.BlazorUI.Widgets;
using ERP.Workflow.Designer.Domain.Shared;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Components;
using Volo.Abp.ObjectMapping;
using ERP.Workflow.Designer.Domain.Shared;
using Microsoft.Extensions.Localization;
using ERP.Workflow.Designer.Domain.Shared.Localization;
using Volo.Abp.AspNetCore.Components.ExceptionHandling;
using Volo.Abp.AspNetCore.Components.Notifications;

namespace ERP.Workflow.Designer.BlazorUI
{
    public partial   class DiagramPanel :  ComponentBase
    {
        [Inject] public NavigationManager? _navigationManager { get; set; }
        [Inject] public IDefinitionAppService?  _definitionAppService { get; set; }
        [Inject] IObjectMapper? ObjectMapper { get; set; }
        [Inject] IStringLocalizerFactory IStringLocalizerFactory { get; set; }
        [Inject] IUserExceptionInformer? IUserExceptionInformer { get; set; }
        [Inject] IUiNotificationService? IUiNotificationService { get; set; }
        [Parameter] public string Id { get; set; }
        public Definition Definition { get; set; } = new Definition();
        public DefinitionSaveDto DefinitionDto { get; set; }
        public Diagram _diagram;                                                                           
        protected FlowNode CurrentSelectNode=new FlowNode();
        protected PropertyBox? _property;
        protected ToolBox? _toolBox;
        protected ToolBoxItem CurrentTool=new ToolBoxItem();
        protected bool IsNew { get; set; }
        public IStringLocalizer? L = null;
        protected override async Task OnInitializedAsync()
        {
            L= IStringLocalizerFactory!.Create<WorkflowDesignerResource>();
            await Task.Run(() =>
            {

                Definition.Id = Guid.Parse(Id);
                DiagramOptions options = new DiagramOptions
                {
                    //   GridSize=50,
                    EnableVirtualization = true,
                    AllowPanning = true,
                    Links = new DiagramLinkOptions
                    {
                        DefaultPathGenerator = PathGenerators.Straight,
                        DefaultRouter = Routers.Orthogonal,
                        DefaultLinkComponent = typeof(LinkWidget),
                        Factory = (diagam, PortModel) => new FlowLink(PortModel),
                        EnableSnapping = false,
                    },
                    Zoom = new DiagramZoomOptions
                    {
                        Enabled = false,
                         
                    },
                };
                _diagram = new Diagram(options);
                _diagram.MouseDoubleClick += _diagram_MouseDoubleClick;
                _diagram.SelectionChanged += _diagram_SelectionChanged;
                _diagram.Changed += _diagram_Changed;
                _diagram.Links.Added += (obj) =>
                {

                    obj.Router = Routers.Orthogonal;
                    obj.PathGenerator = PathGenerators.Straight;
                    obj.TargetMarker = LinkMarker.Arrow;
                };
               _diagram.RegisterModelComponent<LinkLabelModel, LinkLabelWidget>();
                _diagram.RegisterModelComponent<FlowNode, FlowNodeWidget>();
                _diagram.RegisterModelComponent<FlowLink, FlowLinkWidget>();




            });
            IsNew = !(await _definitionAppService!.IsExistAsync(Guid.Parse(Id)));
            if (!IsNew)
            {
                await InitializeDiagram();
            }
            else
            {
                await Init_Data_Test();
            }
            await InvokeAsync(() =>
             {
                 foreach (FlowNode n in _diagram.Nodes)
                 {
                     n.OnTitleChange += (obj, old, n) =>
                     {
                         _property?.Reload(obj);
                     };
                 }
                 _diagram.ZoomToFit();
             });
        }
        protected override Task OnParametersSetAsync()
        {
            return base.OnParametersSetAsync();
        }
        public void  PropertyBox_Change(PropertyChangeEventArg arg)
        {
            if (arg.Data is FlowNode node && arg.FieldName == nameof(FlowNode.NodeType))
            {
                var tool = _toolBox?.Tools.FirstOrDefault(m => m.Name == node.NodeType.ToString());
                node.Icon = tool!.Icon ?? "form";
                node.Rotate = tool!.Rotate;
            }
          
             StateHasChanged();
        }
        private async void DiagramPanel_OnRefresh(RefreshEventArg obj)
        {
            _diagram!.Nodes.Clear();
            _diagram.Links.Clear();
            if (!IsNew)
                await InitializeDiagram();
            else

              await   Init_Data_Test();
        }
        public async void DiagramPanel_OnSave(SaveEventArg obj)
        {
            try
            {
                var input = new DefinitionSaveDto();
                input.Definition = ObjectMapper!.Map<Definition, DefinitionDto>(Definition ?? new Definition());
                input.Links = ObjectMapper.Map<List<FlowLink>, List<LinkDto>>(_diagram!.Links.Select(m => (FlowLink)m).ToList());
                input.Nodes = ObjectMapper.Map<List<FlowNode>, List<NodeDto>>(_diagram.Nodes.Select(m => (FlowNode)m).ToList());

                input.Nodes.ForEach(n =>
                {
                    n.DefinitionId = input.Definition.Id;
                    n.Ports.ForEach(p =>
                    {
                        p.DefinitionId = input.Definition.Id;
                        p?.Links?.ForEach(l =>
                        {
                            l.DefinitionId = input.Definition.Id;
                            l.Labels.ForEach(t => t.DefinitionId = input.Definition.Id);
                            l.Vertices.ForEach(t => t.DefinitionId = input.Definition.Id);
                        });

                    });
                });
                input.Links.ForEach(l =>
                {
                    l.DefinitionId = input.Definition.Id;
                    l.Labels.ForEach(t => t.DefinitionId = input.Definition.Id);
                    l.Vertices.ForEach(t => t.DefinitionId = input.Definition.Id);
                });
                var rs = await _definitionAppService!.SaveAsync(input);
                await IUiNotificationService!.Success("操作成功!", "提示");
                IsNew = false;
                UIEventBus.Emit(EventConst.DesignerList_Refresh, this, new UIEventArgs { });
            }
            catch (Exception ex)
            {
               await  IUserExceptionInformer!.InformAsync(new UserExceptionInformerContext(ex));
            }
        }
        public void DiagramPanel_OnNew(NewEventArg args)
        {
            DiagramPanel_OnSave(null);
            Definition = new Definition();
            Definition.Name = "流程名称"+DateTime.Now.ToString("MMddhh");
            Id = Definition.Id.ToString();
            _diagram!.Nodes.Clear();
            _diagram.Links.Clear();
            IsNew = true;
        }
        private void _menuBox_OnClickTitle(ClickTitleEventArg obj)
        {
            _property?.Reload(Definition);
        }
        private void _diagram_SelectionChanged(SelectableModel obj)
        {
            if (!obj.Selected && obj is NodeModel)
            {
                var node = (FlowNode)_diagram!.Nodes.First(m => m.Id == obj.Id);
                node.Contenteditable = false;
            }
            else if (obj.Selected && obj is NodeModel)
            {
                var node = (FlowNode)_diagram!.Nodes.First(m => m.Id == obj.Id);
                CurrentSelectNode = node;
                _property?.Reload(node);
            }
            else if (obj.Selected && (obj is LinkModel))
            {
                var link = obj as FlowLink;
                if (link == null) return;
                _property?.Reload(link);
            }
            
        }
        private void _diagram_MouseDoubleClick( Model arg1, Microsoft.AspNetCore.Components.Web.MouseEventArgs arg2)
        {
            if (arg1 == null) return;
            var obj = _diagram!.Nodes.FirstOrDefault(m => m.Id == arg1.Id);
            if (arg1 is FlowNode)
            {
                var node = obj as FlowNode;
                node!.Contenteditable = true;
            }
            if (arg1 is FlowLinkModel)
            {

            }
        }
        public async Task InitializeDiagram()
        {
            DefinitionDto = await _definitionAppService!.FindDetailAsync(Guid.Parse(Id));
            Definition = ObjectMapper!.Map<DefinitionDto, Definition>(DefinitionDto.Definition);
            DefinitionDto.Nodes.ForEach(sn =>
            {
                var tn = new FlowNode(sn.Id.ToString(), new Point(sn.X, sn.Y)) {Title = sn.Title ?? "",} ;
                if (sn?.ExtraProperties is not null)
                    sn!.ExtraProperties.ExtraPropertyDataToObject<FlowNode>(tn);
                sn?.Ports.ForEach(p =>
                {
                    var PortAlignment = (PortAlignment)Enum.Parse(typeof(PortAlignment), ((int)p.PortAlignments).ToString() );
                    tn.AddPort(new FlowPortModel(p.Id.ToString(), tn, PortAlignment, new Point(p.X, p.Y), new Size(p.Width, p.Height)) { Initialized = true });
                });
                _diagram.Nodes.Add(tn);
            });
            var nodes = _diagram.Nodes;
          
            DefinitionDto.Links.ForEach(l =>
            {
                var sNode = nodes.First(m => m.Id == l.SourceNodeId.ToString());
                var sPort = sNode!.Ports.First(m => m.Id == l.SourcePortId.ToString());

                var tNode = nodes.First(m => m.Id.ToLower() == l.TargetNodeId.ToString().ToLower());
                var tPort = tNode!.Ports.First(m => m.Id.ToString().ToLower()== l.TargetPortId.ToString().ToLower());

                var ll = new FlowLink(l.Id.ToString(), sPort, tPort);
                ll.Content = string.Join(",", l.Labels);
                ll.Paths = l!.Paths!.Split(",");
                l.ExtraProperties.ExtraPropertyDataToObject<FlowLink>(ll);
                l.Labels.ForEach(b =>
                {
                    Point? offsetPoint = null;
                    if (b.OffsetX.HasValue)
                    {
                        offsetPoint = new Point(b.OffsetX.Value, b!.OffsetY!.Value);
                    }
                    ll.Labels.Add(new LinkLabelModel(ll, b.Id.ToString(), b?.Content ?? "", null, offsetPoint));
                });
                l.Vertices.ForEach(v =>
                {
                        // var jj = new LinkVertexModel(ll);
                        // jj.SetPosition( v.X!.Value, v.Y!.Value);
                        //jj.Id = "111";
                        // ll.Vertices.Add(jj );
                });

                _diagram!.Links.Add(ll);
            });
        }
        private async Task Init_Data_Test()
        {
            var node1 = NewNode(250, 80, "开始");
            var node2 = NewNode(500, 350, "财务审批");
            var node3 = NewNode(600, 100, "结束");

            node1.AddPort(new FlowPortModel(node1, PortAlignment.Bottom, null, new Size(10, 10)));
            node1.AddPort(new FlowPortModel(node1, PortAlignment.Top, null, new Size(10, 10)));
            node1.AddPort(new FlowPortModel(node1, PortAlignment.Left, null, new Size(10, 10)));
            node1.AddPort(new FlowPortModel(node1, PortAlignment.Right, null, new Size(10, 10)));


            node2.AddPort(new FlowPortModel(node2, PortAlignment.Bottom, null, new Size(10, 10)));
            node2.AddPort(new FlowPortModel(node2, PortAlignment.Top, null, new Size(10, 10)));
            node2.AddPort(new FlowPortModel(node2, PortAlignment.Left, null, new Size(10, 10)));
            node2.AddPort(new FlowPortModel(node2, PortAlignment.Right, null, new Size(10, 10)));



            node3.AddPort(new FlowPortModel(node3, PortAlignment.Bottom, null, new Size(10, 10)));
            node3.AddPort(new FlowPortModel(node3, PortAlignment.Top, null, new Size(10, 10)));
            node3.AddPort(new FlowPortModel(node3, PortAlignment.Left, null, new Size(10, 10)));
            node3.AddPort(new FlowPortModel(node3, PortAlignment.Right, null, new Size(10, 10)));





            _diagram!.Nodes.Add(new[] { node1, node2, node3 });
            var link1 = new FlowLink(Guid.NewGuid().ToString(), node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))
            {
                Router = Routers.Orthogonal,
                PathGenerator = PathGenerators.Straight,
                TargetMarker = LinkMarker.Arrow,
            };
            link1.Labels.Add(new LinkLabelModel(link1, "Normal"));
            var link2 = new FlowLink(Guid.NewGuid().ToString(), node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))
            {
                Router = Routers.Orthogonal,
                TargetMarker = LinkMarker.Arrow,
                PathGenerator = PathGenerators.Straight // Smooth results in weird looking links
            };
            link2.Labels.Add(new LinkLabelModel(link2, "Orthogonal"));
            _diagram.Links.Add(new[] { link1, link2 });

            await Task.CompletedTask;
        }
        private void _diagram_Changed()
        {
           
        }
        private NodeModel NewNode(double x, double y)
        {
            var node = new NodeModel(new Point(x, y));
            node.AddPort(PortAlignment.Bottom);
            node.AddPort(PortAlignment.Top);
            node.AddPort(PortAlignment.Left);
            node.AddPort(PortAlignment.Right);
            return node;
        }
        private NodeModel NewNode(double x, double y,string title)
        {
            var node = new FlowNode(new Point(x, y)) { Title=title };
            return node;
        }
        private void Drop(DragEventArgs e)
        {
            if (CurrentTool == null || CurrentTool.ModelData == null) return;
            var position = _diagram!.GetRelativeMousePoint(e.ClientX, e.ClientY);
            var obj = Activator.CreateInstance(CurrentTool.ModelData);
            var o = (obj as FlowNode);
            var tool = _toolBox?.Tools.FirstOrDefault(m => m.Name == CurrentTool!.Name);
            o!.Icon = tool!.Icon ?? "form";
            o.Rotate = tool!.Rotate;
            o.NodeType =(NodeType)Enum.Parse(typeof(NodeType), CurrentTool!.Name??"");



            o!.AddPort(new FlowPortModel(o, PortAlignment.Bottom, null, new Size(10, 10)));
            o!.AddPort(new FlowPortModel(o, PortAlignment.Top, null, new Size(10, 10)));
            o!.AddPort(new FlowPortModel(o, PortAlignment.Left, null, new Size(10, 10)));
            o!.AddPort(new FlowPortModel(o, PortAlignment.Right, null, new Size(10, 10)));

            o!.SetPosition(position.X, position.Y);
            _diagram.Nodes.Add(o);
            _diagram.SelectModel(o, true);

        }
    }
}
